﻿using System.Collections.Generic;
using System.Linq;
using GammaJul.ReSharper.GammaXaml.Psi.Xaml;
using JetBrains.Application.DataContext;
using JetBrains.ReSharper.Feature.Services.ContextNavigation.ContextSearches;
using JetBrains.ReSharper.Feature.Services.ContextNavigation.ContextSearches.BaseSearches;
using JetBrains.ReSharper.Feature.Services.Search.SearchRequests;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Services;
using JetBrains.Util;

namespace GammaJul.ReSharper.GammaXaml.Services.Xaml.Navigation {

	/// <summary>
	/// A custom context search that provides declarations referenced by a <see cref="CaliburnBoundXamlReference"/>.
	/// Without this context search, the default R# behavior when using "Go To Declaration" is to provide only the current declared element,
	/// which, in this case, is a field with a x:Name, thus blocking the possibility to see the method.
	/// </summary>
	[FeaturePart]
	public class CaliburnDeclarationContextSearch : ContextSearchBase<SearchDeclarationsRequest>, IDeclarationContextSearch {

		protected override SearchDeclarationsRequest CreateSearchRequest(IDataContext dataContext, IDeclaredElement declaredElement, IDeclaredElement initialTarget) {
			return new SearchDeclarationsRequest(declaredElement);
		}

		public virtual bool IsGotoDeclarationApplicable(IDeclaredElement declaredElement) {
			return true;
		}

		public virtual SearchDeclarationsRequest GetDeclarationSearchRequest(IDeclaredElement declaredElement) {
			return new SearchDeclarationsRequest(declaredElement);
		}

		protected override bool IsAvailableInternal(IDataContext dataContext) {
			return GetCandidates(dataContext).Any();
		}

		protected override ICollection<DeclaredElementInstance> GetCandidateInstances(IDataContext context, ReferencePreferenceKind kind) {
			ICollection<IReference> references = context.GetData(JetBrains.ReSharper.Psi.Services.DataConstants.REFERENCES);
			if (references == null)
				return EmptyArray<DeclaredElementInstance>.Instance;

			return CandidatesUtil.GetCandidateInstances(kind, references.Where(r => r is CaliburnBoundXamlReference).ToArray());
		}

	}

}